package cn.stylefeng.guns.modular.launchpad.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.guns.core.common.page.LayuiPageFactory;
import cn.stylefeng.guns.core.exceptions.BusinessException;
import cn.stylefeng.guns.modular.app.entity.Member;
import cn.stylefeng.guns.modular.base.state.F;
import cn.stylefeng.guns.modular.base.state.ProConst;
import cn.stylefeng.guns.modular.base.util.RedisUtil;
import cn.stylefeng.guns.modular.fin.entity.Cashflow;
import cn.stylefeng.guns.modular.fin.entity.Currency;
import cn.stylefeng.guns.modular.fin.service.CashflowService;
import cn.stylefeng.guns.modular.fin.service.CurrencyService;
import cn.stylefeng.guns.modular.launchpad.entity.Distribution;
import cn.stylefeng.guns.modular.launchpad.entity.Pledge;
import cn.stylefeng.guns.modular.launchpad.entity.Product;
import cn.stylefeng.guns.modular.launchpad.entity.Receive;
import cn.stylefeng.guns.modular.launchpad.mapper.PledgeMapper;
import cn.stylefeng.guns.modular.launchpad.mapper.ProductMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 新币挖矿-质押赎回Service
 *
 * @author yaying.liu
 * @since 2024-05-11 11:12:18
 */
@Service
public class PledgeService extends ServiceImpl<PledgeMapper, Pledge> {

    @Autowired
    private CurrencyService currencyService;
    @Autowired
    private DistributionService distributionService;
    @Autowired
    private ReceiveService receiveService;
    @Autowired
    private CashflowService cashflowService;

    @Autowired
    private PledgeMapper pledgeMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ProductMapper productMapper;

    /**
     * 质押
     */
    @Transactional(rollbackFor = Exception.class)
    public void impawn(Pledge pledge, String token) {
        if (StrUtil.isEmpty(pledge.getPledgeCoin())) {
            throw new BusinessException("质押币种不能为空");
        }
        if (StrUtil.isEmpty(pledge.getPledgeNum())) {
            throw new BusinessException("质押数量不能为空");
        }
        if (pledge.getProductId() == null) {
            throw new BusinessException("产品id不能为空");
        }
        Member currentMember = getMember(token);
        pledge.setMemberId(currentMember.getMemberId());
        pledge.setCreateTime(new Date());

        // 产品是否支持这个币种
        Product product = productMapper.selectById(pledge.getProductId());
        if (product == null) {
            throw new BusinessException("产品不存在");
        }
        if (!product.getPledgeCoins().contains(pledge.getPledgeCoin())) {
            throw new BusinessException("产品仅支持" + product.getPledgeCoins() + "币种");
        }
        if (product.getEnableFlag() != 1) {
            throw new BusinessException("产品未启动, 不可质押");
        }
        Date now = new Date();
        if (now.after(product.getEndTime())) {
            throw new BusinessException("不在可质押时间内");
        }


        // 现货-币币账户余额是否足够
        Currency currencyCoin = currencyService.getOne(new LambdaQueryWrapper<Currency>()
                .eq(Currency::getMemberId, pledge.getMemberId())
                .eq(Currency::getType, pledge.getPledgeCoin()));

        if (currencyCoin == null) {
            throw new BusinessException("您的币币账户中不存在" + pledge.getPledgeCoin() + "币种");
        }

        if (currencyCoin.getUsedPrice().compareTo(new BigDecimal(pledge.getPledgeNum())) < 0) {
            throw new BusinessException("您的币币账户中" + pledge.getPledgeCoin() + "币种余额不足");
        }

        // 产品id-用户id-质押币种 得到原来的质押记录
        Pledge oldPledge = this.baseMapper.selectOne(new LambdaQueryWrapper<Pledge>()
                .eq(Pledge::getProductId, pledge.getProductId())
                .eq(Pledge::getMemberId, pledge.getMemberId())
                .eq(Pledge::getPledgeCoin, pledge.getPledgeCoin()));


        // 新增质押
        if (oldPledge == null) {
            // 记录质押记录
            this.baseMapper.insert(pledge);
        } else {
            // 原来的质押数量+下单数量
            BigDecimal nowPledgeNum = new BigDecimal(oldPledge.getPledgeNum()).add(new BigDecimal(pledge.getPledgeNum()));
            oldPledge.setPledgeNum(nowPledgeNum.toString());
            oldPledge.setUpdateTime(new Date());
            this.baseMapper.updateById(oldPledge);
        }


        // 记录流水
        BigDecimal beforePrice = currencyCoin.getUsedPrice(); // 原可用额度
        BigDecimal afterPrice = currencyCoin.getUsedPrice().subtract(new BigDecimal(pledge.getPledgeNum())); // 扣减后可用额度
        currencyCoin.setUsedPrice(afterPrice);
        currencyService.updateById(currencyCoin);
        F.me().saveCashflow(pledge.getMemberId(),
                ProConst.WalletBigType.CURRENCY, // 币币账户
                ProConst.CashFlowOpEnum.FLOW_OUT, // 流出
                ProConst.CashFlowTypeEnum.LAUNCHPAD_IMPAWN, // 新币挖矿-质押
                new BigDecimal(pledge.getPledgeNum()), pledge.getPledgeCoin(), new BigDecimal(pledge.getPledgeNum()), pledge.getPledgeCoin(),
                BigDecimal.ZERO, pledge.getPledgeCoin(),
                ProConst.ItemCode.USED,
                pledge.getPledgeCoin(), null, null,
                beforePrice, afterPrice,
                pledge.getMemberId(), pledge.getMemberId());


    }


    public String getPledgeAmount(Long productId, String pledgeCoin, String token) {
        if (productId == null) {
            throw new BusinessException("产品id不能为空");
        }
        if (StrUtil.isEmpty(pledgeCoin)) {
            throw new BusinessException("质押币种不能为空");
        }
        Member currentMember = getMember(token);

        Product product = productMapper.selectById(productId);
        if (product == null) {
            throw new BusinessException("产品不存在");
        }

        // 产品id-用户id-质押币种 得到原来的质押记录
        Pledge oldPledge = this.baseMapper.selectOne(new LambdaQueryWrapper<Pledge>()
                .eq(Pledge::getProductId, productId)
                .eq(Pledge::getMemberId, currentMember.getMemberId())
                .eq(Pledge::getPledgeCoin, pledgeCoin));

        if (oldPledge == null) {
            return "0";
        } else {
            return oldPledge.getPledgeNum();
        }
    }


    /**
     * 赎回
     */
    @Transactional(rollbackFor = Exception.class)
    public void redeem(Pledge pledge, String token) {
        // 校验参数
        if (pledge.getProductId() == null) {
            throw new BusinessException("产品id不能为空");
        }
        if (StrUtil.isEmpty(pledge.getPledgeCoin())) {
            throw new BusinessException("赎回币种不能为空");
        }
        if (StrUtil.isEmpty(pledge.getPledgeNum()) || new BigDecimal(pledge.getPledgeNum()).compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("请填写正确的赎回数量");
        }
        Member currentMember = getMember(token);
        pledge.setMemberId(currentMember.getMemberId());

        // 产品id-用户id-质押币种 得到原来的质押记录
        Pledge oldPledge = this.baseMapper.selectOne(new LambdaQueryWrapper<Pledge>()
                .eq(Pledge::getProductId, pledge.getProductId())
                .eq(Pledge::getMemberId, pledge.getMemberId())
                .eq(Pledge::getPledgeCoin, pledge.getPledgeCoin()));


        // 新增质押
        if (oldPledge == null) {
            throw new BusinessException("您没有质押记录");
        }

        // 原来的质押数量
        BigDecimal oldPledgeNum = new BigDecimal(oldPledge.getPledgeNum());
        // 赎回数量
        BigDecimal redeemNum = new BigDecimal(pledge.getPledgeNum());
        if (oldPledgeNum.compareTo(redeemNum) < 0) {
            throw new BusinessException("赎回数量不能大于质押数量");
        }
        BigDecimal afterPledgeNum = oldPledgeNum.subtract(redeemNum);
        if (afterPledgeNum.compareTo(BigDecimal.ZERO) == 0) {
            afterPledgeNum = BigDecimal.ZERO;
        }
        oldPledge.setPledgeNum(afterPledgeNum.toString());

        oldPledge.setUpdateTime(new Date());
        this.baseMapper.updateById(oldPledge);


        // 现货-币币账户余额增加
        Currency currencyCoin = currencyService.getOne(new LambdaQueryWrapper<Currency>()
                .eq(Currency::getMemberId, pledge.getMemberId())
                .eq(Currency::getType, pledge.getPledgeCoin()));

        if (currencyCoin == null) {
            throw new BusinessException("您的币币账户中不存在" + pledge.getPledgeCoin() + "币种");
        }

        BigDecimal beforePrice = currencyCoin.getUsedPrice(); // 原可用额度
        BigDecimal afterPrice = currencyCoin.getUsedPrice().add(redeemNum); // 增加后可用额度
        currencyCoin.setUsedPrice(afterPrice);
        currencyService.updateById(currencyCoin);

        F.me().saveCashflow(pledge.getMemberId(),
                ProConst.WalletBigType.CURRENCY, // 币币账户
                ProConst.CashFlowOpEnum.FLOW_IN, // 流入
                ProConst.CashFlowTypeEnum.LAUNCHPAD_REDEEM, // 新币挖矿-赎回
                redeemNum, oldPledge.getPledgeCoin(), redeemNum, oldPledge.getPledgeCoin(),
                BigDecimal.ZERO, pledge.getPledgeCoin(),
                ProConst.ItemCode.USED,
                oldPledge.getPledgeCoin(), null, null,
                beforePrice, afterPrice,
                pledge.getMemberId(), pledge.getMemberId());
    }

    private Member getMember(String token) {
        Member currentMember = (Member) redisUtil.get(token);
        if (currentMember == null) {
            throw new BusinessException("用户未登录");
        }
        return currentMember;
    }

    public List<Pledge> getByProductId(Long id) {
        return this.baseMapper.selectList(new LambdaQueryWrapper<Pledge>().eq(Pledge::getProductId, id));
    }

    public IPage<Distribution> getDistributionList(String token) {
        Member member = getMember(token);
        Page<Distribution> pageParam = LayuiPageFactory.defaultPage();

        return distributionService.page(pageParam, new LambdaQueryWrapper<Distribution>()
                .eq(Distribution::getMemberId, member.getMemberId())
                .orderByDesc(Distribution::getCreateTime));
    }

    // 查询个人待领取奖励
    public List<Map<String, Object>> getPendingReward(String token) {
        Member member = getMember(token);


        List<Distribution> list = distributionService.list(new LambdaQueryWrapper<Distribution>()
                .eq(Distribution::getMemberId, member.getMemberId())
                .eq(Distribution::getReceiveStatus, 0));

        List<Map<String, Object>> returnDataList = new ArrayList<>();
        // 按照产品,质押币种分组
        Map<Long, Map<String, List<Distribution>>> distributionGroupMap = list.stream()
                .collect(Collectors.groupingBy(Distribution::getProductId, Collectors.groupingBy(Distribution::getPledgeCoin)));

        for (Map.Entry<Long, Map<String, List<Distribution>>> entry : distributionGroupMap.entrySet()) {
            Long projectId = entry.getKey(); // 产品id 一个产品即一种奖励币 不需再次分组
            Map<String, List<Distribution>> pledgeCoinGroupMap = entry.getValue(); // 质押币种分组

            for (Map.Entry<String, List<Distribution>> pledgeEntry : pledgeCoinGroupMap.entrySet()) {
                String pledgeCoin = pledgeEntry.getKey();// 质押币种

                List<Distribution> distributionList = pledgeEntry.getValue();

                // 累加当前币种数量
                BigDecimal totalNum = distributionList.stream().map(Distribution::getDistributionNum)
                        .map(BigDecimal::new)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                // 累加获取到的分红
                BigDecimal totalReward = distributionList.stream().map(Distribution::getDistributionNum)
                        .map(BigDecimal::new)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                returnDataList.add(new Dict()
                        .set("projectId", projectId)
                        .set("pledgeCoin", pledgeCoin)
                        .set("pledgeNum", totalNum)
                        .set("distributionCoin", distributionList.get(0).getDistributionCoin())
                        .set("distributionNum", totalReward)
                        .set("distributionIdList", distributionList.stream().map(Distribution::getId).collect(Collectors.toList()))
                );
            }
        }


        return returnDataList;
    }

    @Transactional(rollbackFor = Exception.class)
    public void receiveReward(String token) {
        // 查询这个用户的待领取奖励
        List<Map<String, Object>> pendingReward = getPendingReward(token);
        if (CollUtil.isEmpty(pendingReward)) {
            throw new BusinessException("没有待领取的奖励");
        }

        Member member = getMember(token);
        Long memberId = member.getMemberId();


        // 遍历待领取奖励
        for (Map<String, Object> map : pendingReward) {
            List<Long> distributionIdList = Convert.convert(new TypeReference<List<Long>>() {
            }, map.get("distributionIdList"));

            Long projectId = Convert.toLong(map.get("projectId")); // 产品id
            String distributionCoin = Convert.toStr(map.get("distributionCoin")); // 奖励币
            BigDecimal distributionNum = Convert.toBigDecimal(map.get("distributionNum")); // 奖励数量

            Date updateTime = new Date();

            // 将分发记录的状态改为已领取
            distributionService.update(new Distribution(), new LambdaUpdateWrapper<Distribution>()
                    .in(Distribution::getId, distributionIdList)
                    .set(Distribution::getReceiveStatus, 1)
                    .set(Distribution::getReceiveTime, updateTime)
            );


            // 记录领取
            Receive receive = new Receive();
            receive.setProductId(projectId);
            receive.setMemberId(memberId);
            receive.setReceiveCoin(distributionCoin);
            receive.setReceiveNum(distributionNum.toString());
            receive.setCreateTime(new Date());
            receiveService.save(receive);

            // 现货-币币账户余额增加
            Currency currencyCoin = currencyService.getOne(new LambdaQueryWrapper<Currency>()
                    .eq(Currency::getMemberId, memberId)
                    .eq(Currency::getType, distributionCoin));

            if (currencyCoin == null) {
                throw new BusinessException("您的币币账户中不存在" + distributionCoin + "币种");
            }

            BigDecimal beforePrice = currencyCoin.getUsedPrice(); // 原可用额度
            BigDecimal afterPrice = currencyCoin.getUsedPrice().add(distributionNum).setScale(4, RoundingMode.HALF_UP); // 增加后可用额度
            currencyCoin.setUsedPrice(afterPrice);
            currencyService.updateById(currencyCoin);

            F.me().saveCashflow(memberId,
                    ProConst.WalletBigType.CURRENCY, // 币币账户
                    ProConst.CashFlowOpEnum.FLOW_IN, // 流入
                    ProConst.CashFlowTypeEnum.LAUNCHPAD_RECEIVE, // 领取分红
                    distributionNum, distributionCoin, distributionNum, distributionCoin,
                    BigDecimal.ZERO, distributionCoin,
                    ProConst.ItemCode.USED,
                    distributionCoin, null, null,
                    beforePrice, afterPrice,
                    memberId, memberId);

        }

    }

    public IPage<Map<String, Object>> getReceiveList(String token) {
        Member member = getMember(token);
        Long memberId = member.getMemberId();

        Page<Cashflow> pageParam = LayuiPageFactory.defaultPage();
        // 领取记录在流水表中
        IPage<Cashflow> page = cashflowService.page(pageParam, new LambdaQueryWrapper<Cashflow>()
                .eq(Cashflow::getMemberId, memberId)
                .eq(Cashflow::getFlowType, ProConst.CashFlowTypeEnum.LAUNCHPAD_RECEIVE.getCode())
                .orderByDesc(Cashflow::getCreateTime));

        // 包装返回数据
        IPage<Map<String, Object>> result = new Page<>();
        result.setTotal(page.getTotal());
        result.setPages(page.getPages());
        result.setSize(page.getSize());
        result.setCurrent(page.getCurrent());
        result.setRecords(page.getRecords().stream().map(cashflow -> {
            Map<String, Object> map = new Dict();
            map.put("flowId", cashflow.getCashflowId());
            map.put("createTime", cashflow.getCreateTime()); // 领取时间
            map.put("receiveCoin", cashflow.getItemName()); // 领取币种
            map.put("receiveNum", cashflow.getActualPrice()); // 领取数量
            return map;
        }).collect(Collectors.toList()));
        return result;
    }

    public IPage<Pledge> getList(Pledge pledge) {
        Page<Pledge> pageParam = LayuiPageFactory.defaultPage();
        return pledgeMapper.getFlowList(pageParam, pledge);
    }

}
